﻿using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using Microsoft.VisualStudio.Imaging;
using Microsoft.VisualStudio.Shell;
using Red.Scripts.Defines.Utilities;

namespace Language.Intellisense.NavigationBar
{
    public partial class Control : UserControl
    {
        #region Private Data

        private Stub.File _file;
        private int _position;

        private Stub.IScope _previousActiveStub;
        private Stub.IScope _currentActiveStub;

        #endregion Private Data

        #region Public Properties

        public Stub.File File { set { _file = value; UpdateActiveLocation(); } }

        public static ThemeResourceKey PlainTextBackground = new ThemeResourceKey( new Guid( "58e96763-1d3b-4e05-b6ba-ff7115fd0b7b" ), "Plain Text", ThemeResourceKeyType.BackgroundBrush );
        public static ThemeResourceKey IndicatorMarginBackground = new ThemeResourceKey( new Guid( "58e96763-1d3b-4e05-b6ba-ff7115fd0b7b" ), "Indicator Margin", ThemeResourceKeyType.BackgroundBrush );


        public int Position
        {
            set
            {
                _position = value;

                if ( _file == null )
                    return;

                int index = _file.Scopes.LowerBound( value );

                if ( index >= _file.Scopes.Count || index < 0 )
                    _currentActiveStub = null;
                else
                {
                    _currentActiveStub = _file.Scopes.Values[ index ];
                }

                UpdateActiveLocation();
            }
        }

        #endregion Public Properties

        #region C-Tor

        public Control()
        {
            InitializeComponent();
        }

        #endregion C-Tor

        #region Private Methods

        private void UpdateActiveLocation()
        {
            if ( _currentActiveStub == _previousActiveStub && _currentActiveStub?.Scope.Contains( _position ) == true )
                return;

            _activeLocation.Inlines.Clear();

            if ( _file?.Stubs.Count > 0 )
            {
                bool success = UpdateActiveLocation( _icon, _activeLocation.Inlines, _currentActiveStub as Stub.Class ) ||
                                UpdateActiveLocation( _icon, _activeLocation.Inlines, _currentActiveStub as Stub.Function ) ||
                                UpdateActiveLocation( _icon, _activeLocation.Inlines, _currentActiveStub as Stub.Enum );
                if ( !success )
                {
                    _currentActiveStub = null;
                }
            }

            _previousActiveStub = _currentActiveStub;
        }

        private bool UpdateActiveLocation( CrispImage icon, InlineCollection inlines, Stub.Class stub, bool prefix = true )
        {
            if ( stub == null )
                return false;

            if ( stub.Scope.Start.Position.Start > _position )
            {
                return false;
            }

            if ( prefix )
            {
                inlines.Add( CreateInline( "class", Red.Scripts.Defines.Guids.Theme.Category.TextEditor.Guid, Syntax.Colouring.Classification.Name.Keyword ) );
                inlines.Add( new Run( " " ) );
            }

            inlines.Add( CreateInline( stub.Name, Red.Scripts.Defines.Guids.Theme.Category.TextEditor.Guid, Syntax.Colouring.Classification.Name.Class ) );

            icon.Moniker = KnownMonikers.Class;

            return true;
        }

        private bool UpdateActiveLocation( CrispImage icon, InlineCollection inlines, Stub.Function stub )
        {
            if ( stub == null )
                return false;

            if ( stub.Scope.Start.Position.Start > _position )
            {
                _currentActiveStub = stub.Parent;
                return UpdateActiveLocation( icon, inlines, stub.Parent );
            }

            inlines.Add( CreateInline( "function", Red.Scripts.Defines.Guids.Theme.Category.TextEditor.Guid, Syntax.Colouring.Classification.Name.Keyword ) );
            inlines.Add( new Run( " " ) );

            UpdateActiveLocation( icon, inlines, stub.Parent, false );

            icon.Moniker = KnownMonikers.Method;

            if ( stub.Parent != null )
            {
                inlines.Add( new Run( "." ) );
            }

            inlines.Add( CreateInline( stub.Name, Red.Scripts.Defines.Guids.Theme.Category.TextEditor.Guid, Syntax.Colouring.Classification.Name.Function ) );

            return true;
        }

        private bool UpdateActiveLocation( CrispImage icon, InlineCollection inlines, Stub.Enum stub )
        {
            if ( stub == null )
                return false;

            if ( stub.Scope.Start.Position.Start > _position )
            {
                return false;
            }

            inlines.Add( CreateInline( "enum", Red.Scripts.Defines.Guids.Theme.Category.TextEditor.Guid, Syntax.Colouring.Classification.Name.Keyword ) );
            inlines.Add( new Run( " " ) );
            inlines.Add( CreateInline( stub.Name, Red.Scripts.Defines.Guids.Theme.Category.TextEditor.Guid, Syntax.Colouring.Classification.Name.Enum ) );
            
            icon.Moniker = KnownMonikers.Enumeration;

            return true;
        }

        private Inline CreateInline( string text, Guid category, string name )
        {
            ThemeResourceKey key = new ThemeResourceKey( category, name, ThemeResourceKeyType.ForegroundBrush );

            Brush foreground = TryFindResource( key ) as Brush;

            return new Run( text ) { Foreground = foreground };
        }

        private static void DumpKeys( ResourceDictionary dictionary )
        {
            foreach ( var key in dictionary.Keys )
            {
                Debug.Print( $"Key: {key}" );

                ThemeResourceKey trk;
                if ( ( trk = key as ThemeResourceKey ) != null )
                {
                    Debug.Print( $" - Name    : {trk.Name}" );
                    Debug.Print( $" - Category: {trk.Category}" );
                }
            }

            foreach ( ResourceDictionary d in dictionary.MergedDictionaries )
            {
                DumpKeys( d );
            }
        }

        #endregion Private Methods
    }
}
